package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"net"
	"strconv"
	"sync"
	"time"
)

const (
	N = 5 // 房间数量
	M = 4 // 每个房间最多人数
)

// 消息类型常量
const (
	MsgTypeConnect   = "connect"
	MsgTypeClose     = "closeconnect"
	MsgTypeJoinRoom  = "joinroom"
	MsgTypeLeaveRoom = "leaveroom"
	MsgTypeAction    = "actionmsg"
)

// 消息结构体
type Message struct {
	Type     string `json:"type"`
	Content  string `json:"content"`
	UserName string `json:"username"`
}

type Client struct {
	conn     net.Conn
	username string
	roomID   int
	sendChan chan Message
}

type Room struct {
	id      int
	members map[*Client]bool
	mu      sync.Mutex
}

var rooms [N]*Room

func initRooms() {
	for i := 0; i < N; i++ {
		room := &Room{
			id:      i,
			members: make(map[*Client]bool),
			mu:      sync.Mutex{},
		}
		go room.broadcast()
		rooms[i] = room
	}
}

func (r *Room) broadcast() {
	ticker := time.NewTicker(time.Second / 15)
	for range ticker.C {
		r.mu.Lock()
		var messages []Message

		for client := range r.members {
			select {
			case msg := <-client.sendChan:
				messages = append(messages, msg)
			default:
				continue
			}
		}

		if len(messages) > 0 { // 如果有消息需要发送，才进行发送操作。
			jsonData, _ := json.Marshal(messages)
			for client := range r.members {
				client.conn.Write(jsonData)
			}
		}

		r.mu.Unlock()
	}
}

func handleConnection(conn net.Conn) {
	defer conn.Close()

	client := &Client{
		conn:     conn,
		sendChan: make(chan Message, 100),
		roomID:   -1,
	}

	scanner := bufio.NewScanner(conn)

	for scanner.Scan() {
		var msg Message

		err := json.Unmarshal(scanner.Bytes(), &msg)

		if err != nil {
			fmt.Println("Error unmarshalling message:", err)
			continue
		}

		switch msg.Type {

		case MsgTypeConnect:
			client.username = msg.UserName

		case MsgTypeJoinRoom:
			roomID, _ := strconv.Atoi(msg.Content)
			if roomID >= 0 && roomID < N && len(rooms[roomID].members) < M {

				rooms[roomID].mu.Lock()
				rooms[roomID].members[client] = true
				rooms[roomID].mu.Unlock()

				client.roomID = roomID

			} else {

				fmt.Println("Failed to join room", roomID)

			}

		case MsgTypeLeaveRoom:

			if client.roomID != -1 {

				rooms[client.roomID].mu.Lock()
				delete(rooms[client.roomID].members, client)
				rooms[client.roomID].mu.Unlock()

				client.roomID = -1

			}

		case MsgTypeAction:

			if client.roomID != -1 {

				// 将用户名添加到消息中，以便其他客户端知道是谁发送的动作消息。
				msg.UserName = client.username

				rooms[client.roomID].mu.Lock()
				for c := range rooms[client.roomID].members {
					c.sendChan <- msg
				}
				rooms[client.roomID].mu.Unlock()

			}

		case MsgTypeClose:

			return

		default:

			fmt.Println("Unknown message type:", msg.Type)

		}

	}

	if err := scanner.Err(); err != nil {

		fmt.Println("Error reading from connection:", err)

	}
}

func main() {

	initRooms()

	listener, err := net.Listen("tcp", ":20000")
	if err != nil {

		fmt.Println("Error starting server:", err)
		return

	}
	defer listener.Close()

	fmt.Println("Server started on :20000")

	for {

		conn, err := listener.Accept()
		if err != nil {

			fmt.Println("Error accepting connection:", err)
			continue

		}

		go handleConnection(conn)

	}

}
